@file:Suppress("unused")

package com.cug.campus_sh_market_server.util

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.hasAnnotation

object KtWrapperUtil {


    /**
     * 获取支持lambda表达式作为column条件的Kotlin专用KtQueryWrapper
     */
    inline fun <reified TARGET : Any, reified PARAM : Any> getKtQueryWrapper(param: PARAM): KtQueryWrapper<TARGET> {
        val ktQueryWrapper = KtQueryWrapper(TARGET::class.java)
        PARAM::class.declaredMemberProperties
            .asSequence()
            .forEach {
                val propertyName = it.name
                val value = it.get(param)
                if (isPropertyHasAnnotation<PARAM, NeedLike>(propertyName)) {
                    ktQueryWrapper.like(value != null, it, value)
                } else {
                    ktQueryWrapper.eq(value != null, it, value)
                }
            }
        return ktQueryWrapper
    }

    /**
     * 获取Java中作为column条件的QueryWrapper
     * 但由于SAM(Single Abstract Method)的限制，在Kotlin中不支持lambda表达式传值
     * 在Kotlin中使用时只支持 ([Boolean], String!, Any!) 方式的传值
     */
    inline fun <reified TARGET : Any, reified PARAM : Any> getQueryWrapper(param: PARAM): QueryWrapper<TARGET> {
        val queryWrapper = QueryWrapper<TARGET>()
        PARAM::class.declaredMemberProperties
            .asSequence()
            .forEach {
                val propertyName = it.name
                val column = getColumn<PARAM>(propertyName)
                val value = it.get(param)
                if (isPropertyHasAnnotation<PARAM, NeedLike>(propertyName)) {
                    queryWrapper.like(value != null, column, value)
                } else {
                    queryWrapper.eq(value != null, column, value)
                }
            }
        return queryWrapper
    }

    /**
     * 解析字段对应的数据库列名
     * 默认为属性名的下划线形式
     *
     * @param propertyName  属性名称
     * @param <T>        需要解析的类类型
     * @return 字段对应的column名
     */
    inline fun <reified T : Any> getColumn(propertyName: String): String? {
        if (isPropertyHasAnnotation<T, Ignore>(propertyName)) {
            return null
        }
        val property = T::class.declaredMemberProperties
            .asSequence()
            .filter { it.name == propertyName }
            .firstOrNull()
        val findAnnotation = property?.findAnnotation<Column>()
        return findAnnotation?.column ?: UnderlineWithHump.HumpToUnderline(property?.name)
    }

    /**
     * 判断一个类的字段是否标有某个特定的注解
     *
     * @param propertyName   字段名
     * @return 布尔值
     */
    inline fun <reified T : Any, reified E : Annotation> isPropertyHasAnnotation(
        propertyName: String,
    ): Boolean {
        val property = T::class.declaredMemberProperties
            .asSequence()
            .filter { it.name == propertyName }
            .firstOrNull()
        return property?.hasAnnotation<E>() ?: false
    }

    /**
     * 用来标记是否需要使用Like关键字
     */
    @Target(AnnotationTarget.PROPERTY)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class NeedLike

    /**
     * 用来标记column的名字
     */
    @Target(AnnotationTarget.PROPERTY)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class Column(val column: String = "")

    /**
     * 用来标记是否需要忽略
     */
    @Target(AnnotationTarget.PROPERTY)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class Ignore
}
